
package convertTestsToJUnit;

import instrumentation.TrackerCoverage;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.IdentityHashMap;
import java.util.concurrent.ExecutionException;
import java.util.LinkedList;

import executor.ReflectionExecutor;
import ga.GAInfo;
import ga.Individual;
import operations.Operation;
import operations.OperationResult;
import operations.InvokeMethod;
import operations.CreateObject;
import util.GAConfig;
import util.Log;
public class TestSuiteReducer {
	private final GAInfo     gaInfo;

	private static final TestTransformation transform_uselessCons = new TestTransformationPipeline(
			RemoveUselessInitialCons.singleton
	);
	private static final TestTransformation transformSimplifierStatic = new TestTransformationPipeline(
			SimplifierStatic.singleton
	);

	public TestSuiteReducer(GAInfo gaInfo) {
		this.gaInfo=gaInfo;
	}

	public  Map<Integer,Individual>  process(Individual test,Map<Integer, Individual> reducedSuiteBR) {
		 Map<Integer,Individual> suiteBR= new HashMap<Integer, Individual>() ;
		try {
			List<Integer> branchCovered1= this.getCoverage(test);
//			System.out.println("testSize-1: "+ test.getSize() );
//			System.out.println("TestSuiteReducer.process: Wholetest Covered-1:\t\t\t " + branchCovered1);
//			Individual copyofTest=test.clone();
			boolean foundNewBranch=false;
			for(Integer br:branchCovered1){
				if(reducedSuiteBR.containsKey(br))continue;
				foundNewBranch=true;
			}
			if(!foundNewBranch) return suiteBR;	
		
			test = transform_uselessCons.perform(test,gaInfo);
//			branchCovered1= this.getCoverage(test);
//			System.out.println("testSize-2: "+ test.getSize() );
//			System.out.println("TestSuiteReducer.process transform_uselessCons-2:\t\t " + branchCovered1);
			// Analyze a test case and remove invalid operations and set null to reference of return method if it returns null
			test=this.perform(test);
//			branchCovered1= this.getCoverage(test);
//			System.out.println("testSize-3: "+ test.getSize() );
//			System.out.println("TestSuiteReducer.process transform_uselessCons-3:\t\t " + branchCovered1);
			
			final List<Individual> parts = new LinkedList<Individual>();// Splitter.split(gaInfo, test);
			parts.add(test);

			for (Individual part : parts) {
				//part = transform.perform(part,gaInfo);
//				// calculate the coverage for the test and pass it to the optimal test selector
				List<Integer> branchCovered= this.getCoverage(part);
//				System.out.println("TestSuiteReducer.process part:\t " + branchCovered);
				for(Integer br:branchCovered){
					if(suiteBR.containsKey(br))continue;
	//				System.out.println("TestSuiteReducer.process: BR Covered: " + br);
					suiteBR.put(br, part);
				}
			}
		} catch (InterruptedException e) {
			System.err.println("Error while reducing the test: " + e);
		} catch (ExecutionException e) {
			System.err.println("Error while reducing the test: " + e);
		}
		//return this.reducedTestSuite(suiteBR);
	//		System.out.println("TestSuiteReducer.process: suite Covered-5:\t\t " + suiteBR.keySet());

		return suiteBR;
	}
	public  Map<Integer,Individual> reducedTestSuite( Map<Integer,Individual> suiteBR) {
		HashSet<String> individualToString= new HashSet<String>();
		Map<Integer,Individual>  noDuplicateSuite =new HashMap<Integer, Individual>() ;
				for (Integer i : suiteBR.keySet()) {
					Individual test=	suiteBR.get(i);
					if(individualToString.contains(test.toString())) continue;
					noDuplicateSuite.put(i,test);
					individualToString.add(test.toString());
				}
			     System.out.println("reducedTestSuite Remove: " + (suiteBR.size() - noDuplicateSuite.size())
			             + " redundant test cases. ");

				System.out.println ("SUMMARY: " + noDuplicateSuite.size() +
			    " tests needed for total branch coverage:");
//			    if(GAConfig.SaveData)
//			    	Log.log("SUMMARY: " + suiteBR.size() +" tests needed for total branch coverage:");
	return noDuplicateSuite;

	}

	 private   List<Integer> getCoverage(Individual test) {
		 TrackerCoverage tracker=  TrackerCoverage.getTracker();
			tracker.reset();

		List<Integer> branchCovered=new LinkedList<Integer>();
		ReflectionExecutor.execute(gaInfo,test);
	//	for(int i=0;i<gaInfo.getTracker().branches.length;i++){
			for (int branchId :tracker.getTracker().branches){
				branchCovered.add(branchId);
		}
		
		return branchCovered;
	}
	

	private Individual perform(Individual orig) throws InterruptedException, ExecutionException {
		Operation[] ops = new Operation[orig.getStatements().size()];
		orig.getStatements().toArray(ops);

		ops = this.perform(ops);

	      Individual test=new Individual(orig.getInitialConstructors(),Arrays.asList(ops));
	      return test;
	}
	private Operation[] perform(final Operation[] test) {
		List<Operation> ops = new ArrayList<Operation>();
		
		for (Operation op : test) {
			OperationResult info = (OperationResult) op.getInfo(OperationResult.KEY);
			if(info != null) {
				switch(info.getStatus()) {
				case NOT_EXECUTED: // skip invalid & not executed operations
					break;
				case PRECONDITION_ERROR:
					ops.add(op);
					break;

				case POSTCONDITION_ERROR: // trigger a reset after a postcondition error
					ops.add(op);
					break;

				case SUCCESSFUL: // add normal operations as-is
					if(op instanceof InvokeMethod){
						if(info.isResultNull()){
		//				System.out.println("TestSuiteReducer " + op);
							InvokeMethod m=(InvokeMethod)op;
							op= new InvokeMethod(null,m.getThis(),m.getMethod(),m.getValues() ,m.getReplacementObjectParam());
						}

					 }
					ops.add(op);
					break;

				case EXCEPTIONAL: 
					ops.add(op);
					break;
				}

			} else {
				if((op instanceof InvokeMethod || op instanceof CreateObject))
					System.err.println("TestSuiteReduce.perform() An OperationResult is expected, but it is not found");

				ops.add(op);
			}
		}

		return ops.toArray(new Operation[ops.size()]);
	}

}